package com.kwdb.importtool.util;

import com.kwdb.importtool.core.KWDBMigrator;
import com.kwdb.importtool.core.SQLConverter;
import com.kwdb.importtool.core.SQLParser;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理器
 * 处理应用程序中的各种异常，提供用户友好的错误信息
 */
@Slf4j
public class ExceptionHandler {

    // 异常类型映射
    private static final Map<Class<? extends Throwable>, ExceptionInfo> exceptionMappings = new HashMap<>();

    // 用户友好的错误消息
    private static final Map<String, String> userFriendlyMessages = new HashMap<>();

    static {
        initializeExceptionMappings();
        initializeUserFriendlyMessages();
    }

    /**
     * 初始化异常类型映射
     */
    private static void initializeExceptionMappings() {
        // SQL相关异常
        exceptionMappings.put(SQLException.class,
                new ExceptionInfo("数据库错误", "数据库操作失败", ErrorLevel.ERROR));

        exceptionMappings.put(java.sql.SQLTimeoutException.class,
                new ExceptionInfo("查询超时", "数据库查询执行时间过长", ErrorLevel.WARNING));

        exceptionMappings.put(java.sql.SQLSyntaxErrorException.class,
                new ExceptionInfo("SQL语法错误", "SQL语句语法不正确", ErrorLevel.ERROR));

        exceptionMappings.put(java.sql.SQLIntegrityConstraintViolationException.class,
                new ExceptionInfo("数据完整性错误", "违反数据完整性约束", ErrorLevel.ERROR));

        // 文件相关异常
        exceptionMappings.put(java.io.FileNotFoundException.class,
                new ExceptionInfo("文件未找到", "指定的文件不存在", ErrorLevel.ERROR));

        exceptionMappings.put(java.io.IOException.class,
                new ExceptionInfo("文件读写错误", "文件读取或写入失败", ErrorLevel.ERROR));

        // 解析相关异常
        exceptionMappings.put(SQLParser.SQLParseException.class,
                new ExceptionInfo("SQL解析错误", "SQL文件解析失败", ErrorLevel.ERROR));

        exceptionMappings.put(SQLConverter.SQLConversionException.class,
                new ExceptionInfo("SQL转换错误", "SQL语法转换失败", ErrorLevel.ERROR));

        // 迁移相关异常
        exceptionMappings.put(KWDBMigrator.MigrationException.class,
                new ExceptionInfo("迁移错误", "数据库迁移过程失败", ErrorLevel.ERROR));

        // 网络相关异常
        exceptionMappings.put(java.net.ConnectException.class,
                new ExceptionInfo("连接拒绝", "无法连接到数据库服务器", ErrorLevel.ERROR));

        exceptionMappings.put(java.net.UnknownHostException.class,
                new ExceptionInfo("未知主机", "无法解析数据库服务器地址", ErrorLevel.ERROR));

        // 内存相关异常
        exceptionMappings.put(OutOfMemoryError.class,
                new ExceptionInfo("内存不足", "应用程序内存不足，请尝试减小处理数据量", ErrorLevel.FATAL));

        // 默认异常处理
        exceptionMappings.put(Exception.class,
                new ExceptionInfo("应用程序错误", "发生未预期的错误", ErrorLevel.ERROR));
    }

    /**
     * 初始化用户友好消息
     */
    private static void initializeUserFriendlyMessages() {
        // 数据库连接错误
        userFriendlyMessages.put("Communications link failure", "数据库连接失败，请检查网络连接和服务器状态");
        userFriendlyMessages.put("Access denied for user", "数据库访问被拒绝，请检查用户名和密码");
        userFriendlyMessages.put("Unknown database", "数据库不存在，请检查数据库名称");
        userFriendlyMessages.put("Table doesn't exist", "表不存在，请检查表名称");
        userFriendlyMessages.put("Connection refused", "连接被拒绝，请检查数据库服务器是否启动");

        // SQL语法错误
        userFriendlyMessages.put("You have an error in your SQL syntax", "SQL语法错误，请检查SQL语句");
        userFriendlyMessages.put("Unknown column", "未知的列名，请检查列名拼写");

        // 文件错误
        userFriendlyMessages.put("The system cannot find the file specified", "系统找不到指定的文件");
        userFriendlyMessages.put("Permission denied", "文件访问权限被拒绝");

        // 特定错误代码映射（MySQL）
        userFriendlyMessages.put("1045", "数据库访问被拒绝：错误的用户名或密码");
        userFriendlyMessages.put("1049", "数据库不存在：请检查数据库名称");
        userFriendlyMessages.put("2002", "无法连接到数据库服务器：请检查服务器地址和端口");
        userFriendlyMessages.put("2003", "数据库连接被拒绝：请检查服务器是否运行");
        userFriendlyMessages.put("1064", "SQL语法错误：请检查SQL语句格式");
    }

    /**
     * 设置全局未捕获异常处理器
     */
    public static void setupGlobalExceptionHandler() {
        Thread.setDefaultUncaughtExceptionHandler(new GlobalUncaughtExceptionHandler());

        // 设置Swing异常处理器
        if (GraphicsEnvironment.isHeadless()) {
            return;
        }

        try {
            // 对于AWT事件分发线程的异常处理
            System.setProperty("sun.awt.exception.handler", GlobalAWTExceptionHandler.class.getName());
        } catch (SecurityException e) {
            log.warn("无法设置AWT异常处理器（安全限制）");
        }
    }

    /**
     * 处理异常并显示给用户
     */
    public static void handleException(Throwable throwable) {
        handleException(throwable, false);
    }

    /**
     * 处理异常并显示给用户
     * @param throwable 异常对象
     * @param isFatal 是否为致命错误
     */
    public static void handleException(Throwable throwable, boolean isFatal) {
        if (throwable == null) {
            return;
        }

        // 记录日志
        logException(throwable);

        // 获取异常信息
        ExceptionInfo exceptionInfo = getExceptionInfo(throwable);
        String userMessage = getUserFriendlyMessage(throwable);

        // 在EDT线程中显示错误对话框
        if (SwingUtilities.isEventDispatchThread()) {
            showErrorDialog(exceptionInfo, userMessage, throwable, isFatal);
        } else {
            SwingUtilities.invokeLater(() ->
                    showErrorDialog(exceptionInfo, userMessage, throwable, isFatal));
        }
    }

    /**
     * 处理异常但不显示对话框（用于后台任务）
     */
    public static void handleSilentException(Throwable throwable) {
        if (throwable == null) {
            return;
        }

        logException(throwable);
    }

    /**
     * 记录异常日志
     */
    private static void logException(Throwable throwable) {
        if (throwable instanceof SQLException) {
            SQLException sqlEx = (SQLException) throwable;
            log.error("数据库错误 [{}]: {}", sqlEx.getErrorCode(), sqlEx.getMessage(), sqlEx);
        } else if (throwable instanceof KWDBMigrator.MigrationException) {
            log.error("迁移过程错误: {}", throwable.getMessage(), throwable);
        } else if (throwable instanceof SQLParser.SQLParseException) {
            log.error("SQL解析错误: {}", throwable.getMessage(), throwable);
        } else if (throwable instanceof SQLConverter.SQLConversionException) {
            log.error("SQL转换错误: {}", throwable.getMessage(), throwable);
        } else {
            log.error("应用程序错误: {}", throwable.getMessage(), throwable);
        }
    }

    /**
     * 获取异常信息
     */
    private static ExceptionInfo getExceptionInfo(Throwable throwable) {
        // 查找最匹配的异常类型
        Class<?> exceptionClass = throwable.getClass();
        while (exceptionClass != null) {
            ExceptionInfo info = exceptionMappings.get(exceptionClass);
            if (info != null) {
                return info;
            }
            exceptionClass = exceptionClass.getSuperclass();
        }

        // 返回默认异常信息
        return exceptionMappings.get(Exception.class);
    }

    /**
     * 获取用户友好的错误消息
     */
    private static String getUserFriendlyMessage(Throwable throwable) {
        String message = throwable.getMessage();
        if (message == null) {
            return "发生未知错误";
        }

        // 查找匹配的用户友好消息
        for (Map.Entry<String, String> entry : userFriendlyMessages.entrySet()) {
            if (message.contains(entry.getKey())) {
                return entry.getValue();
            }
        }

        // 对于SQLException，检查错误代码
        if (throwable instanceof SQLException) {
            SQLException sqlEx = (SQLException) throwable;
            String errorCode = String.valueOf(sqlEx.getErrorCode());
            if (userFriendlyMessages.containsKey(errorCode)) {
                return userFriendlyMessages.get(errorCode);
            }
        }

        // 返回原始消息或默认消息
        return message.length() > 200 ? message.substring(0, 200) + "..." : message;
    }

    /**
     * 显示错误对话框
     */
    private static void showErrorDialog(ExceptionInfo exceptionInfo, String userMessage,
                                        Throwable throwable, boolean isFatal) {

        String title = exceptionInfo.getTitle();
        String detailedMessage = buildDetailedMessage(throwable);

        // 创建错误消息面板
        JPanel messagePanel = new JPanel(new BorderLayout(10, 10));

        // 主消息
        JTextArea messageArea = new JTextArea(userMessage);
        messageArea.setEditable(false);
        messageArea.setLineWrap(true);
        messageArea.setWrapStyleWord(true);
        messageArea.setBackground(UIManager.getColor("Label.background"));
        messageArea.setFont(UIManager.getFont("Label.font"));

        // 详细信息（可展开）
        JTextArea detailsArea = new JTextArea(detailedMessage);
        detailsArea.setEditable(false);
        detailsArea.setFont(new Font("Monospaced", Font.PLAIN, 11));

        JScrollPane detailsScrollPane = new JScrollPane(detailsArea);
        detailsScrollPane.setPreferredSize(new Dimension(500, 150));
        detailsScrollPane.setBorder(BorderFactory.createTitledBorder("错误详情"));

        // 创建可折叠的详细信息面板
        JPanel detailsPanel = createCollapsiblePanel("显示技术详情", detailsScrollPane);

        messagePanel.add(messageArea, BorderLayout.NORTH);
        messagePanel.add(detailsPanel, BorderLayout.CENTER);

        // 选择对话框类型
        int messageType = isFatal ? JOptionPane.ERROR_MESSAGE : JOptionPane.ERROR_MESSAGE;

        // 显示对话框
        if (isFatal) {
            JOptionPane.showMessageDialog(
                    null, messagePanel, title, messageType);

            // 致命错误，退出应用程序
            System.exit(1);
        } else {
            JOptionPane.showMessageDialog(
                    null, messagePanel, title, messageType);
        }
    }

    /**
     * 创建可折叠的面板
     */
    private static JPanel createCollapsiblePanel(String title, JComponent content) {
        JPanel panel = new JPanel(new BorderLayout());

        JButton toggleButton = new JButton(title);
        toggleButton.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));

        // 初始状态隐藏内容
        content.setVisible(false);

        toggleButton.addActionListener(e -> {
            boolean visible = !content.isVisible();
            content.setVisible(visible);
            toggleButton.setText(visible ? "隐藏技术详情" : "显示技术详情");

            Window window = SwingUtilities.getWindowAncestor(panel);
            if (window != null) {
                window.pack();
            }
        });

        panel.add(toggleButton, BorderLayout.NORTH);
        panel.add(content, BorderLayout.CENTER);

        return panel;
    }

    /**
     * 构建详细错误消息
     */
    private static String buildDetailedMessage(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);

        pw.println("异常类型: " + throwable.getClass().getName());
        pw.println("异常消息: " + throwable.getMessage());
        pw.println();

        // 对于SQLException，显示额外信息
        if (throwable instanceof SQLException) {
            SQLException sqlEx = (SQLException) throwable;
            pw.println("SQL状态: " + sqlEx.getSQLState());
            pw.println("错误代码: " + sqlEx.getErrorCode());
            pw.println();
        }

        pw.println("堆栈跟踪:");
        throwable.printStackTrace(pw);

        return sw.toString();
    }

    /**
     * 处理数据库连接异常
     */
    public static void handleDatabaseConnectionException(SQLException e, String databaseType) {
        String customMessage = String.format("无法连接到%s数据库", databaseType);

        ExceptionInfo info = new ExceptionInfo("连接错误", customMessage, ErrorLevel.ERROR);
        String userMessage = getUserFriendlyMessage(e);

        SwingUtilities.invokeLater(() ->
                showErrorDialog(info, userMessage, e, false));
    }

    /**
     * 处理SQL执行异常
     */
    public static void handleSQLExecutionException(SQLException e, String sql) {
        ExceptionInfo info = new ExceptionInfo("SQL执行错误", "SQL语句执行失败", ErrorLevel.ERROR);

        String userMessage = getUserFriendlyMessage(e);
        if (sql != null && sql.length() > 100) {
            userMessage += "\n\n相关SQL: " + sql.substring(0, 100) + "...";
        } else if (sql != null) {
            userMessage += "\n\n相关SQL: " + sql;
        }

        String finalUserMessage = userMessage;
        SwingUtilities.invokeLater(() ->
                showErrorDialog(info, finalUserMessage, e, false));
    }

    /**
     * 处理文件操作异常
     */
    public static void handleFileOperationException(Exception e, String filePath, String operation) {
        String customMessage = String.format("文件%s失败: %s", operation, filePath);

        ExceptionInfo info = new ExceptionInfo("文件错误", customMessage, ErrorLevel.ERROR);
        String userMessage = getUserFriendlyMessage(e);

        SwingUtilities.invokeLater(() ->
                showErrorDialog(info, userMessage, e, false));
    }

    /**
     * 异常信息类
     */
    private static class ExceptionInfo {
        private final String title;
        private final String description;
        private final ErrorLevel level;

        public ExceptionInfo(String title, String description, ErrorLevel level) {
            this.title = title;
            this.description = description;
            this.level = level;
        }

        public String getTitle() { return title; }
        public String getDescription() { return description; }
        public ErrorLevel getLevel() { return level; }
    }

    /**
     * 错误级别枚举
     */
    public enum ErrorLevel {
        INFO,
        WARNING,
        ERROR,
        FATAL
    }

    /**
     * 全局未捕获异常处理器
     */
    private static class GlobalUncaughtExceptionHandler implements UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            log.error("未捕获的异常在线程: {}", t.getName(), e);

            boolean isFatal = e instanceof OutOfMemoryError ||
                    e instanceof VirtualMachineError;

            handleException(e, isFatal);
        }
    }

    /**
     * AWT事件分发线程异常处理器
     */
    public static class GlobalAWTExceptionHandler {
        public void handle(Throwable e) {
            log.error("AWT事件分发线程异常", e);
            handleException(e, false);
        }
    }

    /**
     * 安全地执行操作，捕获并处理异常
     */
    public static boolean executeSafely(SafeRunnable runnable) {
        return executeSafely(runnable, null);
    }

    public static boolean executeSafely(SafeRunnable runnable, String errorMessage) {
        try {
            runnable.run();
            return true;
        } catch (Exception e) {
            if (errorMessage != null) {
                log.error(errorMessage, e);
            }
            handleException(e);
            return false;
        }
    }

    /**
     * 安全地执行操作并返回结果
     */
    public static <T> T executeSafely(SafeSupplier<T> supplier, T defaultValue) {
        return executeSafely(supplier, defaultValue, null);
    }

    public static <T> T executeSafely(SafeSupplier<T> supplier, T defaultValue, String errorMessage) {
        try {
            return supplier.get();
        } catch (Exception e) {
            if (errorMessage != null) {
                log.error(errorMessage, e);
            }
            handleException(e);
            return defaultValue;
        }
    }

    /**
     * 安全运行接口
     */
    @FunctionalInterface
    public interface SafeRunnable {
        void run() throws Exception;
    }

    /**
     * 安全供应接口
     */
    @FunctionalInterface
    public interface SafeSupplier<T> {
        T get() throws Exception;
    }

    /**
     * 验证异常是否可恢复
     */
    public static boolean isRecoverable(Throwable throwable) {
        if (throwable instanceof OutOfMemoryError) {
            return false;
        }

        if (throwable instanceof VirtualMachineError) {
            return false;
        }

        if (throwable instanceof SQLException) {
            SQLException sqlEx = (SQLException) throwable;
            // 某些SQL错误是可恢复的
            int errorCode = sqlEx.getErrorCode();
            return errorCode != 1045 && errorCode != 1049; // 认证和数据库不存在错误不可恢复
        }

        return true;
    }

    /**
     * 获取异常的根本原因
     */
    public static Throwable getRootCause(Throwable throwable) {
        Throwable cause = throwable;
        while (cause.getCause() != null) {
            cause = cause.getCause();
        }
        return cause;
    }
}